﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Idlesoft.Utils;
using System.Reflection;

namespace Idlesoft.Cli
{
	internal class CliAttribute : ICliAttribute
	{
		public bool Option { get { return !this.BuiltIn && !this.Required; } }

		public bool BuiltIn { get { return this.MemberInfo.DeclaringType.Equals(typeof(CliBuilder)); } }

		public bool Required { get; set; }

		public FlagTypes FlagTypes { get; set; }

		public ValueTypes ValueTypes
		{
			get
			{
				if (this.MemberInfoType == null)
					return ValueTypes.Undef;
				if (typeof(Boolean).Equals(this.MemberInfoType))
					return ValueTypes.Boolean;
				else if (this.MemberInfoType.IsArray)
					return ValueTypes.Multiple;
				else
					return ValueTypes.Single;
			}
		}

		public object[] Args { get; set; }

		public string Name { get; set; }

		public string Description { get; set; }

		public string[] Flags { get; set; }

		public object[] Values
		{
			get
			{
				if (!this.Args.IsNullOrEmpty())
				{
					if (this.Args.Length == 1)
						return null;
					else
						return this.Args.Tail();
				}
				return null;
			}
			set
			{
				if (!this.Args.IsNullOrEmpty())
					this.Args = this.Args.Take(1).Plus<object>(value).ToArray();
				else
					throw new ArgumentNullException("Values.set can't be called when Args is null.");
			}
		}

		public object Default { get; set; }

		public MemberInfo MemberInfo { get; set; }

		public Type MemberInfoType
		{
			get
			{
				if (this.MemberInfo is PropertyInfo)
					return (this.MemberInfo as PropertyInfo).PropertyType;
				else if (this.MemberInfo is FieldInfo)
					return (this.MemberInfo as FieldInfo).FieldType;
				return null;
			}
		}

		public bool IsAssigned(object target)
		{
			bool isAssigned = false;
			if (target != null)
			{
				if (this.MemberInfo is PropertyInfo)
					isAssigned = (this.MemberInfo as PropertyInfo).GetValue(target, null) != this.MemberInfoType.Default();
				else if (this.MemberInfo is FieldInfo)
					isAssigned = (this.MemberInfo as FieldInfo).GetValue(target) != this.MemberInfoType.Default();
			}
			return isAssigned;
		}

		public bool MatchesArg(string arg)
		{
			return this.Args != null && this.Args.Any(a => a.Equals(arg));
		}

		public override string ToString()
		{
			return string.Format("Name={0} Types={1} Flags={2} Values={3}", this.Name, this.FlagTypes, this.Flags.Flatten("|"), this.Values.Flatten("|"));
		}

		public override bool Equals(object obj)
		{
			bool equals = false;
			CliAttributeAttribute rhs = obj as CliAttributeAttribute;
			if (rhs != null)
			{
				if (this.MemberInfo != null && rhs.MemberInfo != null)
					equals = this.MemberInfo.Equals(rhs.MemberInfo);
			}
			return equals;
		}

		public override int GetHashCode() { return base.GetHashCode(); }
	}
}
